/*	--- 方法引用 --- 


使用【Lambda表达式】，我们就可以不必编写【FunctionalInterface接口的实现类】，从而简化代码：*/
Arrays.sort(array, (s1, s2) -> {
	return s1.compareTo(s2);
});



/*-------------------

#	方法引用(静态方法)


实际上，除了【Lambda表达式】，我们还可以直接传入【方法引用】。

例如：*/
import java.util.Arrays;

public class Main {
	public static void main(String[] args) {
		String[] array = new String[] {"Apple", "Orange", "Banana", "Lemon"};

		Arrays.sort(array, Main::cmp); 
			// Mai::cmp 方法引用(静态方法)

		System.out.println(String.join(", ", array));
	}

	static int cmp(String s1, String s2) {
		return s1.compareTo(s2);
	}
}


/*
上述代码在【Arrays.sort()中】直接传入了【静态方法cmp的引用】，用【Main::cmp】表示。

-----------------


因此，所谓【方法引用】，是指如果【某个方法签名】和【接口】恰好一致，就可以直接传入【方法引用】。

因为【Comparator<String>接口】定义的方法是【int compare(String, String)】，和【静态方法int cmp(String, String)】相比，除了【方法名】外，【方法参数】一致，【返回类型】相同。

因此，我们说两者的【方法签名】一致，可以直接把【方法名】作为【Lambda表达式】传入：*/
Arrays.sort(array, Main:cmp);



/*----------------------------

#	方法引用(实例方法)


注意：在这里，【方法签名】只看【参数类型】和【返回类型】
					  不看【方法名称】，也不看【类的继承关系】。

我们再看看如何引用【实例方法】。

如果我们把【代码】改写如下：*/
import java.util.Arrays;

public class Main {
	public static void main(String[] args) {
		String[] array = new String[] { "Apple", "Orange", "Banana", "Lemon" };
		Arrays.sort(array, String::compareTo);
			// String::compareTo 方法引用(实例方法)
		System.out.println(String.join(", ", array));
	}
}



/*
不但可以【编译通过】，而且【运行结果】也是一样的

这说明【String.compareTo()方法】也符合【Lambda定义】。

观察【String.compareTo()的方法定义】：*/
public final class String {
	public int compareTo(String o) {
		//...
	}
}


/*
这个【方法的签名】只有【一个参数】，为什么和【int Comparator<String>.compare(String, String) 】能匹配呢？


因为【实例方法】有一个【隐含的this参数】

【String类的compareTo()方法】在实际调用的时候，【第一个隐含参数】总是传入【this】，相当于【静态方法】：*/
public static int compareTo(this, String o);

/*

所以，【String.compareTo()方法】也可作为【方法引用】传入。


------------------------------------------------------------


#	方法引用(构造方法)


除了可以引用【静态方法】和【实例方法】，我们还可以引用【构造方法】。

我们来看一个例子：如果要把一个 List<String> 转换为 List<Person>，应该怎么办？*/
class Person {
	String name;

	public Person(String name) {
		this.name = name;
	}
}

List<Sting> names = List.of("Bob", "Alice", "Tim");
List<Person> persons = ???


// 传统的做法
//先定义一个【 ArrayList<Person> 】，然后用【for循环】填充【这个List】：
List<String> names = List.of("Bob", "Alice", "Tim");
	// List.of(); return 不可变的List 
	// List.asList(); return 可变的List
List<Person> persons = new ArrayList<>();

for (String name : names) {
	persons.add(new Person(name));
}


/* 方法引用(构造方法)
 可以更简单地实现 String 到 Person 的转换，我们可以引用【Person的构造方法】：*/
import java.util.*;
import java.util.stream.*;

public class Main {
	public static void main(String[] args) {
		List<String> names = List.of("Bob", "Alice", "Tim");

		// 引用 Person的构造方法
		List<Person> persons = names.stream().map(Person::new).collect(Collectors.toList());
			// List.stream().map(Person::new).collect(Collectors.toList());
		System.out.println(persons);
	}
}

class Person {
	String name;

	public Person(String name) {
		this.name = name;
	}

	public String toString() {
		return "Person: " + this.name;
	}
}


/*
后面我们会讲到Stream的map()方法。

现在我们看到，这里的map()需要传入的【FunctionalInterface的定义】是：*/
@FunctionalInterface
public Inteface Function<T, R> {
	R applay(T t);
}


/*
把【泛型】对应上就是【方法签名Person apply(String)】

String.map(Person)
即【传入参数T = String】，【返回类型R = Person】。

而【Person类的构造方法】恰好满足这个条件，因为【构造方法的参数是String】，而【构造方法】虽然没有【return语句】，但它会【隐式地返回this实例】，【类型】就是Person，因此，此处可以【引用构造方法】。

【构造方法的引用】写法是【类名::new】，因此，此处传入【Person::new】。



==================================================


#	----- 方法引用 の 小结 -----


1. 【FunctionalInterface】允许传入：

	· 接口的实现类（传统写法，代码较繁琐）；
	· Lambda表达式（只需列出参数名，由编译器推断类型）；
	· 符合方法签名的静态方法；
	· 符合方法签名的实例方法（实例类型被看做第一个参数类型）；
	· 符合方法签名的构造方法（实例类型被看做返回类型）。

2. 【FunctionalInterface】不强制【继承关系】，不需要【方法名称】相同，只要求【方法参数（类型和数量）】与【方法返回类型】相同，即认为【方法签名】相同。


3. 【方法签名】包括【返回类型】和【参数列表-Type和Type顺序】,不包括【访问修饰符】和【异常类型】以及【继承关系】

	方法签名 = 返回类型 + 传入【类型-参数】列表(顺序和Type)

4. 【实例方法】有一个【隐含的this参数】，且为参数中第一个
	例如：
		public int compareTo(String o) {
			//... 
		}

		Arrays.sort(array, String::compareTo);

		// 上方方法签名, 实际调用中, 等同于下方
		public static int compareTo(this, String o);


5. 引用【构造方法】
	例子：如果要把一个List<String>转换为List<Person>

传统做法：
	List<String> names = List.of("Bob", "Alice", "Tim");
	List<Person> persons = new ArrayList<>();

	for (String name : names) {
	    persons.add(new Person(name));
	}


引用"Person的构造方法"：
	List<String> names = List.of("Bob", "Alice", "Tim");
	List<Person> persons = names.stream().map(Person::new).collect(Collectors.toList());

class Person {
    String name;

    public Person(String name) {
        this.name = name;
    }

    public String toString() {
        return "Person:" + this.name;
    }
}


6. List<Person> persons = names.stream().map(Person::new).collect(Collectors.toList());

	.collect()是【收集器】,专门用于对【运行内存中的数据流】进行收集,并在(...)括号中,给出处理方法

一,collect是一个终端操作,它接收的参数是将流中的元素累积到汇总结果的各种方式(称为收集器)

二,预定义收集器包括将流元素归约和汇总到一个值.

三,预定义收集器可以用groupby对流中元素进行分组或者用partitioningBy进行分区

四,收集器可以高效的复合起来,进行多级分组,多级分区和归约

五,可以自己实现collector接口进行定义自己的收集器


--------------------------------


工厂方法		返回类型		用于

toList		List<T>		把流中所有元素收集到List中

示例:List<Menu> menus=Menu.getMenus.stream().collect(Collectors.toList())

toSet		Set<T>		把流中所有元素收集到Set中,删除重复项

示例:Set<Menu> menus=Menu.getMenus.stream().collect(Collectors.toSet())

toCollection	Collection<T>	把流中所有元素收集到给定的供应源创建的集合中

示例:ArrayList<Menu> menus=Menu.getMenus.stream().collect(Collectors.toCollection(ArrayList::new))

Counting    Long    	计算流中元素个数

示例:Long count=Menu.getMenus.stream().collect(counting);

SummingInt  Integer     对流中元素的一个整数属性求和

示例:Integer count=Menu.getMenus.stream().collect(summingInt(Menu::getCalories))

averagingInt  Double    计算流中元素integer属性的平均值

示例:Double averaging=Menu.getMenus.stream().collect(averagingInt(Menu::getCalories))

Joining       String    连接流中每个元素的toString方法生成的字符串

示例:String name=Menu.getMenus.stream().map(Menu::getName).collect(joining(“, ”))

maxBy    	Optional<T>    一个包裹了流中按照给定比较器选出的最大元素的optional
如果为空返回的是Optional.empty()

示例:Optional<Menu> fattest=Menu.getMenus.stream().collect(maxBy(Menu::getCalories))

minBy		Optional<T>    一个包裹了流中按照给定比较器选出的最大元素的optional
如果为空返回的是Optional.empty()

示例: Optional<Menu> lessest=Menu.getMenus.stream().collect(minBy(Menu::getCalories))

Reducing    归约操作产生的类型    从一个作为累加器的初始值开始,利用binaryOperator与流中的元素逐个结合,从而将流归约为单个值

示例:int count=Menu.getMenus.stream().collect(reducing(0,Menu::getCalories,Integer::sum));

collectingAndThen    转换函数返回的类型    包裹另一个转换器,对其结果应用转换函数

示例:Int count=Menu.getMenus.stream().collect(collectingAndThen(toList(),List::size))

groupingBy    Map<K,List<T>>    根据流中元素的某个值对流中的元素进行分组,并将属性值做为结果map的键

示例:Map<Type,List<Menu>> menuType=Menu.getMenus.stream().collect(groupingby(Menu::getType))

partitioningBy    Map<Boolean,List<T>>    根据流中每个元素应用谓语的结果来对项目进行分区

示例:Map<Boolean,List<Menu>> menuType=Menu.getMenus.stream().collect(partitioningBy(Menu::isType));









*/






